/***********************************************************************
	Copyright @ Potevio Technologizes Co., Ltd.  All rights rreserved. 
 	File Name :	ftp.c
	Author : MingLiang.lu   Version:1.0 		Date:2015-09-16
	Description:
		为基于2416平台的智能光配线架(ODF)软件编写的一个ftp客户端软件
	Others:
		该文件的所有注释的格式遵循《代码整洁之道》(Robert C.Martin) 
		
		注意：不必要的头文件不应该被包含，用不的的变量要即使清除

	History:
		1.0 
			Date:2015-09-16
			Author: MingLiang.lu  	
			Modification:
 				1).增加系统错误定位捕捉函数sys_perror，依据参数2传递的
				是否是系统调用，而决定是否使用perror函数。


 ***********************************************************************/
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#include "dfu_update.h"

struct DFU_UPDATE dfu;

#if 0
uint8_t update_frames[SZ_16];
uint8_t update_status[SZ_16];
uint8_t update_frames_stats[SZ_16][SZ_6];
uint8_t update_frames_trays[SZ_16][SZ_6];
uint8_t update_trays_stats[SZ_16][SZ_6];

#endif 
uint8_t data_packet[70][520];

static void sys_perror(const char *errinfo, uint8_t sys_error, int line)
{
	fprintf(stderr, "[Error] :%s, line %d " ,__FILE__, line);
	if(sys_perror)
		perror(errinfo);
}

// 发送命令，参数为 socket号 、 命令标示 和 命令参数
static void sendCommand(int sock_fd, const char* cmd, const char* info)
{
    char buf[SZ_512] = {0};

    strcpy(buf, cmd);
    strcat(buf, info);
    strcat(buf, "\r\n");
    if (send(sock_fd, buf, strlen(buf), 0) < 0){
		sys_perror("send", 1, __LINE__);
	}
}

/*
 *
 *
 * status: clear
 */
static int getReplyCode(int sockfd)
{
    int r_code, bytes;
    char buf[SZ_512] = {0}, nbuf[5] = {0};

    if ((bytes = read(sockfd, buf, SZ_512 - 2)) > 0) {
        r_code = atoi(buf);
        buf[bytes] = '\0';
    }else{
        return -1;
	}

    if (buf[3] == '-') {
        char* newline = strchr(buf, '\n');
        if (*(newline+1) == '\0') {
            while ((bytes = read(sockfd, buf, SZ_512 - 2)) > 0) {
                buf[bytes] = '\0';
                if (atoi(buf) == r_code)
                    break;
            }
        }
    }

    if (r_code == PASV_MODE){
        char* begin = strrchr(buf, ',')+1;
        char* end = strrchr(buf, ')');
        strncpy(nbuf, begin, end - begin);
        nbuf[end-begin] = '\0';
        dfu.data_port = atoi(nbuf);
        buf[begin-1-buf] = '\0';
        end = begin - 1;
        begin = strrchr(buf, ',')+1;
        strncpy(nbuf, begin, end - begin);
        nbuf[end-begin] = '\0';
        dfu.data_port += 256 * atoi(nbuf);
    }

    return r_code;
}


// 下载服务器的一个文件
static int cmd_get(int fd, char* cmd,unsigned char *src)
{
    int i = 0, bytes = 0;
	int data_sock = 0;
	char filename[SZ_256];
	unsigned char buf[520];
	unsigned char packetnums = 0x0;
	struct sockaddr_in data_client;

	data_sock = -1;
	dfu.data_port = 0;
    sendCommand(fd, "PASV", "");
    if (getReplyCode(fd) != PASV_MODE){
        return 0;
    }

    data_client.sin_family = AF_INET;
    data_client.sin_port = htons(dfu.data_port);
	data_client.sin_addr.s_addr = dfu.serverip[3]<<24 | dfu.serverip[2]<<16 | dfu.serverip[1]<<8 | dfu.serverip[0];
	
    if((data_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0){
        sys_perror("socket", 1, __LINE__);
	}

    if(connect(data_sock, (struct sockaddr*)&data_client, sizeof(data_client)) < 0){
        sys_perror("connect", 1, __LINE__);
	}

    sendCommand(fd, "TYPE ", "I");
    getReplyCode(fd);

	if(src[347] == 0x01){
		sendCommand(fd, "CWD ", "ODF");
	    getReplyCode(fd);
	    sendCommand(fd, "CWD ", "44b0");
	    getReplyCode(fd);	
	}else if(src[347] == 0x02){
		sendCommand(fd, "CWD ", "ODF");
	    getReplyCode(fd);
	    sendCommand(fd, "CWD ", "146");
	    getReplyCode(fd);
	}else if(src[347] == 0x03){
		sendCommand(fd, "CWD ", "ODF");
	    getReplyCode(fd);
	    sendCommand(fd, "CWD ", "410");
	    getReplyCode(fd);
	}

	strcpy(filename,cmd);
    sendCommand(fd, "RETR ",filename);
	if(getReplyCode(fd) != DOWN_READY){
        sys_perror("NULL", 0, __LINE__);
        close(fd);
        return 0;
	}

	if(src[347] == 0x01){
		return 0;
	}

	memset(buf,0xff,sizeof(buf));
	dfu.data_packet_num = 0;
	packetnums = 0;
	for(i = 0; i < 60;i++){
		memset(&data_packet[i],0xff,520);
	}

    while((bytes = read(data_sock, buf, SZ_512)) > 0){
		memcpy(&data_packet[packetnums][0],buf,SZ_512);	
		packetnums += 1;	
		memset(buf,0xff,sizeof(buf));
		dfu.data_packet_num++;
		usleep(50000);
	}
	
    close(data_sock);
	
	return 0;
}

// 退出
static void cmd_quit(int fd)
{
    sendCommand(fd, "QUIT", "");
    if (getReplyCode(fd) == CONTROL_CLOSE){
		printf("Download Done!");
		/* FTP Transmit Finished! and quit successfully*/
	}else{
		/* FTP Transmit Finished, but quit Failed !!!*/
	}
}


static void split_filename(unsigned char *file,int len)
{
	int i = len,j = 0;
	char version[20];
	unsigned char idex = 0;

	for(;i > 0;i--){
		if(*(file+i) == 0x5c)
			break;
	}

	strncpy(&dfu.filename[0],&file[0] + i + 1,len-(i-1));
	strncpy(&version[0],&file[0] + i + 1,len-(i-1));
	dfu.filename[len-i+1] = '\0';

	idex = 0;
	for(j = 0;j < len-i+1;j++){
		if(version[j] == 0x5f){//5f:is symbol for "_"
			dfu.fversion[idex++] = version[j+1]-0x30;
			dfu.fversion[idex++] = version[j+2]-0x30;
		}
	}
}

/*
 *decription: 自己写的ftp客户端软件，适配于njpt2416主控板用
 *
 *
 */
int ftp_client(unsigned char *username, unsigned char *password, unsigned char* serverip, 
		unsigned char* serverport,unsigned char *filepath,unsigned char *src)
{
	int control_sockfd = 0, retry_times = 0, rc = 0;
	struct sockaddr_in cmd_client;

	/* 清除ftp使用过程中需要登录是用到的:用户名，密码，下载的文件名，下载文件路径 */
	memset((void *)&dfu, 0x0, sizeof(struct DFU_UPDATE));

	/* 记录ftp使用过程中需要登录是用到的:用户名，密码，下载的文件名，下载文件路径 */
	strncpy(&dfu.username[0],&username[0]+1,*(username+0));
	strncpy(&dfu.password[0],&password[0]+1,*(password+0));
	strncpy(&dfu.filepath[0],&filepath[0]+2,filepath[0]<<8|filepath[1]);
	strncpy(&dfu.serverip[0],&serverip[0], 4);
	dfu.cmd_port = serverport[0]*100 + serverport[1];

	/* 从网管给出的数据中，提取出文件名,并记录本次升级文件的版本号 */
	split_filename(filepath,filepath[0]<<8|filepath[1]);
	
	/* 尝试连接远程的ftp服务器,建立命令传送通道 */
    cmd_client.sin_family = AF_INET;
    cmd_client.sin_port   = htons(dfu.cmd_port);
	cmd_client.sin_addr.s_addr = dfu.serverip[3]<<24 | dfu.serverip[2]<<16 | dfu.serverip[1]<<8 | dfu.serverip[0];
    if((control_sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
		sys_perror("socket", 1, __LINE__);
		return FTP_ERROR;
	}

//	printf("IP add = %d.%d.%d.%d\n",dfu.serverip[0],dfu.serverip[1],dfu.serverip[2],dfu.serverip[3]);
//	printf("port add = %d\n",dfu.cmd_port);

    if(connect(control_sockfd, (struct sockaddr*)&cmd_client, sizeof(cmd_client)) < 0){
		sys_perror("connect", 1, __LINE__);
		return FTP_ERROR;
	}

    if(getReplyCode(control_sockfd) != SERVICE_READY){
		sys_perror(NULL, 0, __LINE__);
		return FTP_ERROR;
	}
	
	/* 连接上之后，尝试登入到远程的ftp服务器上,如果尝试登录3次都失败了，就要回复网管，文件下载失败！ */ 
	for(retry_times = 0; retry_times < 3; retry_times ++){

	    sendCommand(control_sockfd, "USER ", &dfu.username[0]);
    	if(getReplyCode(control_sockfd) == NEED_PASSWORD){
        	sendCommand(control_sockfd, "PASS ", &dfu.password[0]);
        	if(getReplyCode(control_sockfd) != LOGIN_SUCS){ 
				/* 登录失败！ */		
				sleep(1);
        	}else{
				/* 登录成功！*/
				break;
        	}
    	}else{
			sleep(1);
		} 
	}
   
	/* 登录成功后，下载本次升级所需要的文件 */
	rc = cmd_get(control_sockfd, &dfu.filename[0], src);
	if(src[347] == 0x01){
		/* don't close control_sockfd,because we need transfer data before close */
		return 0;
	}

	printf("Now say goodbye!\n");
	/* 下载文件完成后，跟ftp服务器说声goodbye :) */

	cmd_quit(control_sockfd);
    close(control_sockfd);

	return 0;

}

